﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using NUnit.Framework;
using XcoAppSpaces.Core.Ports;

namespace test.XcoAppSpaces.Core.Ports
{
	[TestFixture]
	public class PortSetTests
	{
		[Test]
		[ExpectedException(typeof(ArgumentException))]
		public void PortSet_with_duplicate_types_throws_exception()
		{
			//Act
			var portSet = new PortSet<int, string, string>();
		}

		[Test]
		public void Posts_are_correctly_relayed()
		{
			//Arrange
			var portSet = new PortSet<int, string, double>();
			
			//Act
			portSet.Post(1);
			portSet.Post("test");
			portSet.Post(1.23);

			//Assert
			Assert.That(portSet.P0.Receive().Result, Is.EqualTo(1));
			Assert.That(portSet.P1.Receive().Result, Is.EqualTo("test"));
			Assert.That(portSet.P2.Receive().Result, Is.EqualTo(1.23));
		}

		[Test]
		[ExpectedException(typeof(ArgumentException))]
		public void Post_with_invalid_type_throws_exception()
		{
			//Arrange
			var portSet = new PortSet<int, string, double>();

			//Act
			portSet.PostUnknownType(DateTime.Now);
		}

		[Test]
		public void Posts_with_unknown_type_are_correctly_relayed()
		{
			//Arrange
			var portSet = new PortSet<int, string, double>();

			//Act
			portSet.PostUnknownType(1);
			portSet.PostUnknownType("test");
			portSet.PostUnknownType(1.23);

			//Assert
			Assert.That(portSet.P0.Receive().Result, Is.EqualTo(1));
			Assert.That(portSet.P1.Receive().Result, Is.EqualTo("test"));
			Assert.That(portSet.P2.Receive().Result, Is.EqualTo(1.23));
		}

		[Test]
		public void Receiver_receives_on_all_ports()
		{
			//Arrange
			var portSet = new PortSet<int, string, double>();
			var receivedItems = new List<object>();
			
			//Act
			Action<object> receiver = _ => receivedItems.Add(_);
			portSet.RegisterUnknownTypeReceiver(receiver);
			portSet.Post(1);
			SpinWait.SpinUntil(() => receivedItems.Count == 1, 500);
			portSet.Post("test");
			SpinWait.SpinUntil(() => receivedItems.Count == 2, 500);
			portSet.Post(1.23);
			SpinWait.SpinUntil(() => receivedItems.Count == 3, 500);
			
			//Assert
			Assert.That(receivedItems, Has.Count.EqualTo(3));
			Assert.That(receivedItems[0], Is.EqualTo(1));
			Assert.That(receivedItems[1], Is.EqualTo("test"));
			Assert.That(receivedItems[2], Is.EqualTo(1.23));
		}

		[Test]
		public void Operators_are_working_correctly()
		{
			//Arrange
			var portSet = new PortSet<int, string, double>();

			//Act
			Port<int> port0 = portSet;
			Port<string> port1 = portSet;
			Port<double> port2 = portSet;

			//Assert
			Assert.That(port0, Is.EqualTo(portSet.P0));
			Assert.That(port1, Is.EqualTo(portSet.P1));
			Assert.That(port2, Is.EqualTo(portSet.P2));
		}
	}
}
